package com.suket.jnn;

import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

public class BackpropagationEngine {

  static Logger logger = Logger.getLogger(BackpropagationEngine.class);

  public static boolean forward( Network network , double[] initValues ) {
    boolean result = false;
    if ( !NetworkSupport.verifyNetwork(network) ) {
      logger.log(Level.WARN, "Failed to forward , found invalid network");
      return result;
    }
    if ( !setNodeValues(network.getInputLayer(), initValues) ) {
      logger.log(Level.WARN, "Failed to forward , can not put init values");
      return result;
    }

    Layer leftLayer = network.getInputLayer();
    Layer rightLayer = null;

    List layers = network.getHiddenLayers();
    Iterator iterL = layers.iterator();
    while ( iterL.hasNext() ) {
      rightLayer = (Layer) iterL.next();
      forward(leftLayer, rightLayer);
      leftLayer = rightLayer;
    }

    rightLayer = network.getOutputLayer();
    forward(leftLayer, rightLayer);

    result = true;
    return result;
  }

  private static boolean forward( Layer leftLayer , Layer rightLayer ) {
    boolean result = false;
    if ( ( leftLayer == null ) || ( rightLayer == null ) ) {
      return result;
    }
    List leftNodes = leftLayer.getNodes();
    List rightNodes = rightLayer.getNodes();
    if ( ( leftNodes == null ) || ( rightNodes == null ) ) {
      return result;
    }

    Iterator iterRN = rightNodes.iterator();
    while ( iterRN.hasNext() ) {
      Node rightNode = (Node) iterRN.next();
      List rightWeights = rightNode.getWeights();
      double ze = 0;
      Iterator iterLN = leftNodes.iterator();
      Iterator iterRW = rightWeights.iterator();
      while ( iterLN.hasNext() && iterRW.hasNext() ) {
        Node leftNode = (Node) iterLN.next();
        Double rightWeight = (Double) iterRW.next();
        if ( ( leftNode == null ) || ( rightWeight == null ) ) {
          break;
        }
        ze += leftNode.getFe() * rightWeight.doubleValue();
      }
      rightNode.setFe(SigmoidFunction.activation(ze));
    }

    return result;
  }

  public static boolean backward( Network network ) {
    boolean result = false;
    if ( !NetworkSupport.verifyNetwork(network) ) {
      logger.log(Level.WARN, "Failed to backward , found invalid network");
      return result;
    }

    return result;
  }

  private static boolean setNodeValues( Layer layer , double[] values ) {
    boolean result = false;

    if ( layer == null ) {
      logger.log(Level.WARN, "Found null layer");
      return result;
    }

    if ( ( values == null ) || ( values.length < 1 ) ) {
      logger.log(Level.WARN, "Found null / empty values");
      return result;
    }

    List listNodes = layer.getNodes();

    if ( listNodes == null ) {
      logger.log(Level.WARN, "Found null list nodes");
      return result;
    }

    if ( values.length != listNodes.size() ) {
      logger.log(Level.WARN, "Found size of values "
          + "doesn't match with layer nodes");
      return result;
    }

    result = true;

    Iterator iterNodes = listNodes.iterator();
    int idx = 0;
    while ( iterNodes.hasNext() ) {
      Node node = (Node) iterNodes.next();
      if ( node == null ) {
        continue;
      }
      if ( idx >= values.length ) {
        break;
      }
      node.setFe(values[idx]);
      idx++;
    }

    return result;
  }

}
